package pers.bc.utils.ftp;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.sshd.sftp.client.SftpClient;

import com.google.gson.Gson;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;

import pers.bc.utils.Bean.BeanUtil;
import pers.bc.utils.constant.IPubCons;
import pers.bc.utils.file.FileUtilbc;
import pers.bc.utils.pub.CollectionUtil;
import pers.bc.utils.pub.JsonUtil;
import pers.bc.utils.pub.JudgeAssertUtil;
import pers.bc.utils.pub.LoggerUtil;
import pers.bc.utils.pub.PropertiesUtil;
import pers.bc.utils.pub.PubEnvUtil;
import pers.bc.utils.pub.StringUtil;

/**
 * 
 ** Sftp協議下載工具類
 * 
 * @qualiFild pers.bc.utils.ftp.SftpDownloader.java<br>
 * @author：LiBencheng<br>
 *                        Ftp
 * @date Created on 2025年2月27日<br>
 * @version 1.0<br>
 */
public class SftpDownloader
{
    private static String folderName = "sftplogs";
    private static LoggerUtil logUtil = LoggerUtil.getInstance(folderName);
    
    public static String HOST = "host";
    public static String PORT = "port";
    public static String USER_NAME = "userName";
    public static String PASSWORD = "password";
    public static String PRIVATE_KEY = "privateKey";
    public static String PASSPHRASE = "passphrase";
    public static String REMOTE_FILE_PATH = "remoteFilePath";
    public static String LOCAL_FILE_PATH = "localFilePath";
    
    /**
     * *********************************************************** <br>
     * *说明：<br>
     * 
     * @see <br>
     * @param args <br>
     * @void <br>
     * @methods pers.bc.utils.ftp.SftpDownloader#main <br>
     * @author LiBencheng <br>
     * @date Created on 2025年2月27日 <br>
     * @time 18:17:07 <br>
     * @version 1.0 <br>
     *************************************************************          <br>
     */
    public static void main(String[] args)
    {
        try
        {
            
            String yhhdFileName = "D:\\ProgramData\\yonyou\\gongshangtouzi\\temp\\"//
                + "0200004519000100324_20221101_22305000011_0000#0045#00307#27656246_00001.pdf";
            String properPatch = "D:\\ProgramData\\yonyou\\gongshangtouzi\\icbcparam.properties";
            Map<String, String> map = PropertiesUtil.getAllProperties(properPatch);
            // if (PubEnvUtil.isEmpty(map.get(SftpDownloader.LOCAL_FILE_PATH)))
            // map.put(SftpDownloader.LOCAL_FILE_PATH, FileUtilbc.getWorkServerPath() + File.separator +
            // "yhhdTempFile" //
            // + File.separator + yhhdFileName);
            // else
            // map.put(SftpDownloader.LOCAL_FILE_PATH, map.get(SftpDownloader.LOCAL_FILE_PATH)//
            // + File.separator + yhhdFileName);
            //
            // if (!PubEnvUtil.isEmpty(map.get(SftpDownloader.REMOTE_FILE_PATH)))
            // map.put(SftpDownloader.REMOTE_FILE_PATH, map.get(SftpDownloader.REMOTE_FILE_PATH)//
            // + File.separator + yhhdFileName);
            // String name ="id_rsa.pub";
            // String privateKey =
            // StreamUtil.InputStreamTOString(StreamUtil.getFileInputStream("D:\\ProgramData\\yonyou\\gongshangtouzi\\doc\\电子回单主动推送Ebillsend（新银企CA）\\key(新银企ca通用数据
            // 文件服务)\\"+name));
            //
            // map.put(SftpDownloader.PRIVATE_KEY, privateKey);
            map.put(SftpDownloader.PRIVATE_KEY, "D:\\ProgramData\\yonyou\\gongshangtouzi\\id_rsa");
            
            ChannelSftp sftpChannelClient = getSftpChannelClient(map);
            
            System.err.println(sftpChannelClient);
            
        }
        catch (Throwable e)
        {
            logUtil.exception(e);
        }
        
    }
    
    public static void jschDownloadFileViaSftp(Map pram, List<String> fileNames) throws Throwable
    {
        jschDownloadFileViaSftp(JsonUtil.transObj2Josn(pram), fileNames);
    }
    
    public static void jschDownloadFileViaSftp(Map pram) throws Throwable
    {
        jschDownloadFileViaSftp(JsonUtil.transObj2Josn(pram));
    }
    
    /**
     * *********************************************************** <br>
     * *说明：Sftp协议下载 <br>
     * 参数 { "host": "sftp.example.com", "port": 22, "username": "user", "password": "pass", "privateKey":
     * "path/to/privatekey", "remoteFilePath": "/remote/path/file.txt", "localFilePath":
     * "/local/path/file.txt" }<br>
     * 
     * @see jsch.jar<br>
     * @param jsonParams <br>
     * @void <br>
     * @methods pers.bc.utils.ftp.SftpDownloader#downloadFileViaSftp <br>
     * @author LiBencheng <br>
     * @date Created on 2025年2月27日 <br>
     * @time 18:14:56 <br>
     * @version 1.0 <br>
     *************************************************************          <br>
     */
    public static void jschDownloadFileViaSftp(String jsonParams) throws Throwable
    {
        Session session = null;
        ChannelSftp sftpChannel = null;
        try
        {
            // 解析JSON参数
            SftpConfig config = getSftpConfig(new Gson().fromJson(jsonParams, LinkedHashMap.class));
            sftpChannel = getSftpChannelClient(config);
            // 下载文件
            sftpChannel.get(config.getRemoteFilePath(), config.getLocalFilePath());
            debuglog(config, "00、File downloaded successfully");
        }
        catch (Exception e)
        {
            JudgeAssertUtil.throwExceptionDir(e, folderName);
        }
        finally
        {
            // 关闭连接
            disconnect(sftpChannel);
        }
    }
    
    public static void jschDownloadFileViaSftp(String jsonParams, List<String> fileNames) throws Throwable
    {
        
        ChannelSftp sftpChannel = null;
        try
        {
            SftpConfig config = BeanUtil.parseVO(jsonParams, SftpConfig.class, Boolean.FALSE);
            sftpChannel = getSftpChannelClient(config);
            
            // 解析JSON参数
            // SftpConfig config = mapper.readValue(jsonParams, SftpConfig.class);
            for (int i = 0; i < PubEnvUtil.getSize(fileNames); i++)
            {
                String fileName = fileNames.get(i);
                // 下载文件
                sftpChannel.get(config.getRemoteFilePath() + File.separator + fileName,
                    config.getLocalFilePath() + File.separator + fileName);
                debuglog(config, "File downloaded [" + fileName + "] successfully! ");
            }
        }
        catch (Exception e)
        {
            JudgeAssertUtil.throwExceptionDir(e, folderName);
        }
        finally
        {
            // 关闭连接
            disconnect(sftpChannel);
        }
    }
    
    /**
     * *********************************************************** <br>
     * *说明： 使用此方法，用完必须調用disconnect关闭Session <br>
     * 
     * @see <br>
     * @param jsonParams
     * @return
     * @throws Throwable <br>
     * @ChannelSftp <br>
     * @methods pers.bc.utils.ftp.SftpDownloader#getSftpChannelClient <br>
     * @author LiBencheng <br>
     * @date Created on 2025年2月28日 <br>
     * @time 11:12:32 <br>
     * @version 1.0 <br>
     *************************************************************          <br>
     */
    public static ChannelSftp getSftpChannelClient(String jsonParams) throws Throwable
    {
        SftpConfig config = null;
        Map pram = new Gson().fromJson(jsonParams, LinkedHashMap.class);
        // try
        // {
        // config = BeanUtil.parseVO(jsonParams, SftpConfig.class, Boolean.FALSE);
        // }
        // catch (Exception e)
        // {
        // config = getSftpConfig(pram);
        // }
        config = getSftpConfig(pram);
        return getSftpChannelClient(config);
    }
    
    /**
     * *********************************************************** <br>
     * *说明： 使用此方法，用完必须調用disconnect关闭Session <br>
     * 
     * @see <br>
     * @param jsonParams
     * @return
     * @throws Throwable <br>
     * @ChannelSftp <br>
     * @methods pers.bc.utils.ftp.SftpDownloader#getSftpChannelClient <br>
     * @author LiBencheng <br>
     * @date Created on 2025年2月28日 <br>
     * @time 11:12:32 <br>
     * @version 1.0 <br>
     *************************************************************          <br>
     */
    public static ChannelSftp getSftpChannelClient(Map pram) throws Throwable
    {
        SftpConfig config = null;
        // try
        // {
        // config = BeanUtil.parseVO(pram, SftpConfig.class, Boolean.FALSE);
        // }
        // catch (Exception e)
        // {
        // config = getSftpConfig(pram);
        // }
        config = getSftpConfig(pram);
        return getSftpChannelClient(config);
    }
    
    /**
     * *********************************************************** <br>
     * *说明： 使用此方法，用完必须調用disconnect关闭Session <br>
     * 
     * @see <br>
     * @param jsonParams
     * @return
     * @throws Throwable <br>
     * @ChannelSftp <br>
     * @methods pers.bc.utils.ftp.SftpDownloader#getSftpChannelClient <br>
     * @author LiBencheng <br>
     * @date Created on 2025年2月28日 <br>
     * @time 11:12:32 <br>
     * @version 1.0 <br>
     *************************************************************          <br>
     */
    public static ChannelSftp getSftpChannelClient(SftpConfig config) throws Throwable
    {
        Session session = null;
        ChannelSftp sftpChannel = null;
        try
        {
            // 解析JSON参数
            // SftpConfig config = BeanUtil.parseVO(jsonParams,SftpConfig.class,Boolean.FALSE);
            // SftpConfig config = mapper.readValue(jsonParams, SftpConfig.class);
            debuglog(config, "1、Sftp服务器的參數配置如下：" + JsonUtil.transObj2Josn(CollectionUtil.transBean2Map(config, true)));
            // 验证必要参数
            validateConfig(config);
            // 准备本地目录
            // prepareLocalDirectory(config.getLocalFilePath());
            FileUtilbc.createPaths(config.getLocalFilePath());
            // 创建JSch会话
            debuglog(config, "2、Create JSch session and SSH configure session parameters....");
            JSch jsch = new JSch();
            // jsch.setConfig("kex", "diffie-hellman-group1-sha1");
            session = jsch.getSession(config.getUsername(), config.getHost(), config.getPort());
            session.setConfig("debug", "true");
            session.setConfig("StrictHostKeyChecking", "no"); // 忽略主机密钥检查（仅用于测试）
            session.setConfig("PreferredAuthentications", "publickey"); // 仅使用公钥认证方式
            // session.setConfig("kex","diffie-hellman-group1-sha1");
            // session.setConfig("IdentityFile",config.getPrivateKey()); // 确保这是正确的路径
            // session.setConfig("ServerAliveInterval", "60000"); // 设置服务器活跃检查间隔
            // session.setPassword("your_passphrase"); // 如果密钥有密码，设置密码
            
            // 设置认证方式
            if (config.getPrivateKey() != null) 
                setupKeyAuthentication(jsch, config);
            else
                session.setPassword(config.getPassword());
            
            // 配置会话参数
            Properties configSession = new Properties();
            configSession.put("StrictHostKeyChecking", "no"); // 忽略主机密钥检查（仅用于测试）
            session.setConfig(configSession);
            debuglog(config, "3、Start establishing SSH link....");
            // 建立连接
            session.connect();
            debuglog(config, "4、SSH session connected....");
            
            // 创建SFTP通道
            sftpChannel = (ChannelSftp) session.openChannel("sftp");
            sftpChannel.connect();
            debuglog(config, "5、SFTP channel opened.....");
        }
        catch (Exception e)
        {
            // 关闭连接
            disconnect(sftpChannel);
            JudgeAssertUtil.throwExceptionDir(e, folderName);
        }
        // finally
        // {
        // // 关闭连接
        // if (sftpChannel != null && sftpChannel.isConnected())
        // sftpChannel.disconnect();
        // if (session != null && session.isConnected())
        // session.disconnect();
        // }
        
        return sftpChannel;
    }
    
    /**
     * *********************************************************** <br>
     * *说明：关闭连接 <br>
     * 
     * @see <br>
     * @param sftpChannel
     * @throws JSchException <br>
     * @void <br>
     * @methods pers.bc.utils.ftp.SftpDownloader#disconnect <br>
     * @author LiBencheng <br>
     * @date Created on 2025年2月28日 <br>
     * @time 11:18:23 <br>
     * @version 1.0 <br>
     *************************************************************          <br>
     */
    public static void disconnect(ChannelSftp sftpChannel)
    {
        Session session = null;
        // 关闭连接
        if (sftpChannel != null && sftpChannel.isConnected())
        {
            try
            {
                session = sftpChannel.getSession();
            }
            catch (JSchException e)
            {
                logUtil.exception("關閉连接时，获取Session异常：", e);
            }
            sftpChannel.disconnect();
        }
        if (session != null && session.isConnected())
        {
            session.disconnect();
        }
    }
    
    /**
     * *********************************************************** <br>
     * *说明：校验必填参数 <br>
     * 
     * @see <br>
     * @param config <br>
     * @void <br>
     * @methods pers.bc.utils.ftp.SftpDownloader#validateConfig <br>
     * @author LiBencheng <br>
     * @date Created on 2025年2月27日 <br>
     * @time 19:12:42 <br>
     * @version 1.0 <br>
     *************************************************************          <br>
     */
    private static void validateConfig(SftpConfig config)
    {
        if (PubEnvUtil.isEmpty(config.getHost())) throw new IllegalArgumentException("Host is required");
        if (PubEnvUtil.isEmpty(config.getUsername())) throw new IllegalArgumentException("Username is required");
        if (PubEnvUtil.isEmpty(config.getRemoteFilePath())) throw new IllegalArgumentException("Remote file path is required");
        if (PubEnvUtil.isEmpty(config.getLocalFilePath())) throw new IllegalArgumentException("Local file path is required");
        if (PubEnvUtil.isEmpty(config.getPassword()) && PubEnvUtil.isEmpty(config.getPrivateKey()))
            throw new IllegalArgumentException("Either password or privateKey must be provided");
        
    }
    
    /**
     * *********************************************************** <br>
     * *说明： 准备本地目录<br>
     * 
     * @see <br>
     * @param localFilePath <br>
     * @void <br>
     * @methods pers.bc.utils.ftp.SftpDownloader#prepareLocalDirectory <br>
     * @author LiBencheng <br>
     * @date Created on 2025年2月27日 <br>
     * @time 19:13:42 <br>
     * @version 1.0 <br>
     *************************************************************          <br>
     */
    private static void prepareLocalDirectory(String localFilePath)
    {
        File localFile = new File(localFilePath);
        File parentDir = localFile.getParentFile();
        if (parentDir != null && !parentDir.exists())
        {
            if (!parentDir.mkdirs())
            {
                throw new RuntimeException("Failed to create local directory: " + parentDir.getAbsolutePath());
            }
        }
    }
    
    /**
     * *********************************************************** <br>
     * *说明：设置认证方式 <br>
     * 
     * @see <br>
     * @param jsch
     * @param config
     * @throws JSchException <br>
     * @void <br>
     * @methods pers.bc.utils.ftp.SftpDownloader#setupKeyAuthentication <br>
     * @author LiBencheng <br>
     * @date Created on 2025年2月27日 <br>
     * @time 19:13:54 <br>
     * @version 1.0 <br>
     *************************************************************          <br>
     */
    private static void setupKeyAuthentication(JSch jsch, SftpConfig config) throws JSchException
    {
        if (config.getPassphrase() != null && !config.getPassphrase().isEmpty())
        {
            jsch.addIdentity(config.getPrivateKey(), config.getPassphrase());
        }
        else
        {
            jsch.addIdentity(config.getPrivateKey());
        }
    }
    
    /**
     * *********************************************************** <br>
     * *说明：校验Hash <br>
     * 
     * @see <br>
     * @param config
     * @throws Exception <br>
     * @void <br>
     * @methods pers.bc.utils.ftp.SftpDownloader#verifyFileHash <br>
     * @author LiBencheng <br>
     * @date Created on 2025年2月27日 <br>
     * @time 19:14:34 <br>
     * @version 1.0 <br>
     *************************************************************          <br>
     */
    private static void verifyFileHash(SftpConfig config) throws Exception
    {
        String[] hashParts = config.getVerifyHash().split(":");
        String algorithm = hashParts[0];
        String expectedHash = hashParts[1];
        
        byte[] fileBytes = Files.readAllBytes(Paths.get(config.getLocalFilePath()));
        MessageDigest digest = MessageDigest.getInstance(algorithm);
        byte[] actualHash = digest.digest(fileBytes);
        
        StringBuilder sb = new StringBuilder();
        for (byte b : actualHash)
        {
            sb.append(String.format("%02x", b));
        }
        
        if (!sb.toString().equals(expectedHash))
        {
            throw new SecurityException("文件哈希校验失败");
        }
    }
    
    /**
     * *********************************************************** <br>
     * *说明：利用通道下载文件 <br>
     * 
     * @see <br>
     * @param sftp
     * @param config
     * @throws IOException <br>
     * @void <br>
     * @methods pers.bc.utils.ftp.SftpDownloader#downloadFile <br>
     * @author LiBencheng <br>
     * @date Created on 2025年2月27日 <br>
     * @time 19:15:25 <br>
     * @version 1.0 <br>
     *************************************************************          <br>
     */
    private static void downloadFile(SftpClient sftp, SftpConfig config) throws IOException
    {
        Path localPath = Paths.get(config.getLocalFilePath());
        Files.createDirectories(localPath.getParent());
        
        try (InputStream remoteFile = sftp.read(config.getRemoteFilePath()))
        {
            Files.copy(remoteFile, localPath);
        }
    }
    
    private static void debuglog(SftpConfig config, String msg)
    {
        
        if (PubEnvUtil.equals(IPubCons.Y, config.getPrintlog().toLowerCase())
            || PubEnvUtil.equals(IPubCons.TRUE, config.getPrintlog().toLowerCase()))
        {
            logUtil.info(msg);
            System.out.println(msg);
        }
    }
    
    public static SftpConfig getSftpConfig(final Map pram)
    {
        return new SftpConfig()
        {
            {
                setHost(StringUtil.valueOfEmpty(pram.get(SftpConfig.HOST)));
                setPort(Integer.parseInt(StringUtil.valueOfEmpty(pram.get(SftpConfig.PORT))));
                setUsername(StringUtil.valueOfEmpty(pram.get(SftpConfig.USERNAME)));
                setPrivateKey(StringUtil.valueOfEmpty(pram.get(SftpConfig.PRIVATE_KEY)));
                setPassword(StringUtil.valueOfEmpty(pram.get(SftpConfig.PASSWORD)));
                setPassphrase(StringUtil.valueOfEmpty(pram.get(SftpConfig.PASSPHRASE)));
                setRemoteFilePath(StringUtil.valueOfEmpty(pram.get(SftpConfig.REMOTE_FILE_PATH)));
                setLocalFilePath(StringUtil.valueOfEmpty(pram.get(SftpConfig.LOCAL_FILE_PATH)));
                setPrintlog(StringUtil.valueOfEmpty(pram.get(SftpConfig.PRINT_LOG)));
                setVerifyHash(StringUtil.valueOfEmpty(pram.get(SftpConfig.VERIFYHASH)));
            }
        };
    }
    
    /**
     * 
     ** Sftp 配置参数类
     * 
     * @qualiFild pers.bc.utils.ftp.SftpDownloader.java<br>
     * @author：LiBencheng<br>
     * @date Created on 2025年2月27日<br>
     * @version 1.0<br>
     */
    public static class SftpConfig
    {

        static String HOST = "host";
        static String PORT = "port";
        static String USERNAME = "userName";
        static String PASSWORD = "password";
        static String PRIVATE_KEY = "privateKey";
        static String PASSPHRASE = "passphrase";
        static String REMOTE_FILE_PATH = "remoteFilePath";
        static String LOCAL_FILE_PATH = "localFilePath";
        static String PRINT_LOG = "printlog";
        static String VERIFYHASH = "verifyHash";
        
        private String host;
        private int port = 22;
        private String username;
        private String password;
        private String privateKey;
        private String passphrase;
        private String remoteFilePath;
        private String localFilePath;
        private String printlog;
        private String verifyHash;
         
        public String getPrintlog()  {  return printlog; }
        public void setPrintlog(String printlog)  {  this.printlog = printlog;    }
        
        public String getVerifyHash() {  return verifyHash;   }
        public void setVerifyHash(String verifyHash) { this.verifyHash = verifyHash;  }

        // Getters and Setters
        public String getHost() { return host; }
        public void setHost(String host) { this.host = host; }
        
        public int getPort() { return port; }
        public void setPort(int port) { this.port = port; }
        
        public String getUsername() { return username; }
        public void setUsername(String username) { this.username = username; }
        
        public String getPassword() { return password; }
        public void setPassword(String password) { this.password = password; }
        
        public String getPrivateKey() { return privateKey; }
        public void setPrivateKey(String privateKey) { this.privateKey = privateKey; }
        
        public String getPassphrase() { return passphrase; }
        public void setPassphrase(String passphrase) { this.passphrase = passphrase; }
        
        public String getRemoteFilePath() { return remoteFilePath; }
        public void setRemoteFilePath(String remoteFilePath) { this.remoteFilePath = remoteFilePath; }
        
        public String getLocalFilePath() { return localFilePath; }
        public void setLocalFilePath(String localFilePath) { this.localFilePath = localFilePath; }
    }
}
